//
// Created by benxb on 2021/11/15.
//

#ifndef BENXDB_TABLE_H
#define BENXDB_TABLE_H

#include <string>
#include <utility>
#include <vector>
#include <unordered_map>
#include "column.h"
#include "common/config.h"

namespace benxdb {

class Table {
public:
    explicit Table(std::string table_name, std::vector<Column> columns) :
        table_name_(std::move(table_name)), columns_(std::move(columns)), lsn_(1), flush_lsn_(0) {
        bytes_size_ = 0;
        for (int i = 0; i < columns_.size(); i++) {
            columns_[i].SetOffset(i);
            columns_[i].SetBytesOffset(bytes_size_);
            bytes_size_ += columns_[i].GetLength();
        }
    };

    explicit Table(std::string table_name, std::vector<Column> columns, std::unordered_map<std::string, std::string> custom_data) :
            table_name_(std::move(table_name)), columns_(std::move(columns)), custom_data_(std::move(custom_data)),
            lsn_(1), flush_lsn_(0) {
        bytes_size_ = 0;
        for (int i = 0; i < columns_.size(); i++) {
            columns_[i].SetOffset(i);
            columns_[i].SetBytesOffset(bytes_size_);
            bytes_size_ += columns_[i].GetLength();
        }
    };

    /**
     * try load from table meta file first
     * @param table_name
     */
    explicit Table(const std::string& table_name);

    ~Table();

    /**
     * Get table name
     * @return
     */
    std::string GetName() {
        return table_name_;
    }

    /**
     * Get offset of column given column name
     * @param column_name
     * @return offset if > 0; -1 means not found
     */
    int GetColumnIndex(const std::string& column_name);

    /**
     * Get column by index
     * @param column index
     * @return true if success
     */
    Column GetColumn(uint32_t column_index);

    /**
     * Save table meta data_ into disk
     * @return true if success
     */
    bool SaveMetaData();

    void SaveLSN();

    /**
     * Add custom data_ for whole table
     * @param kv_pair
     */
    void SetCustomData(const std::string& key, const std::string& value);

    bool GetCustomData(const std::string& key, std::string& val);

    /**
     * Add column into the table
     * @param col
     */
    void AddColumn(Column col) {
        col.SetOffset(columns_.size());
        col.SetBytesOffset(bytes_size_);
        columns_.push_back(col);
        bytes_size_ += col.GetLength();
    }

    void SetAllColumn(std::vector<Column>& columns) {
        columns = columns_;
    }

    std::vector<Column>& GetAllColumn() {
        return columns_;
    }

    void GetSchema(std::vector<TypeId>& schema) {
        for (auto & column : columns_) {
            schema.push_back(column.GetType());
        }
    }

    int GetColumnSize() {
        return columns_.size();
    }

    int GetColumnCount() {
        return columns_.size();
    }

    std::unordered_map<std::string, std::string> GetAllCustomData() {
        return custom_data_;
    }

    void IncreaseLSN() {
        lsn_++;
        SaveLSN();
    }

    void SetLSN(uint32_t lsn) {
        lsn_ = lsn;
        SaveLSN();
    }

    uint32_t GetLSN() const { return lsn_; }

    void SetFlushLSN(uint32_t lsn) {
        flush_lsn_ = lsn;
        SaveLSN();
    }

    uint32_t GetFlushLSN() const { return flush_lsn_; }

    uint32_t GetBytesSize() const { return bytes_size_; }

private:
    static constexpr int LSN_OFFSET = 0;

    std::string table_name_;
    // table column data_
    std::vector<Column> columns_;
    // user defined data_
    std::unordered_map<std::string, std::string> custom_data_;
    // last active log sequence number
    uint32_t lsn_{};
    // last flush log sequence number
    // from flush_lsn_+1 to active lsn are frozen
    uint32_t flush_lsn_{};

    uint32_t bytes_size_{};
};

}


#endif //BENXDB_TABLE_H
